package AsLayers.BaseType
{
	public class Bounds
	{
		
		/** 
 * APIFunction: fromString
 * Alternative constructor that builds a new Bounds from a 
 *     parameter string
 * 
 * Parameters: 
 * str - {String}Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
 * 
 * Returns:
 * {<Bounds>} New <OpenLayers.Bounds> object built from the 
 *                       passed-in String.
 */
public static function fromString(str:String):Bounds {
    var bounds:Array = str.split(",");
    return Bounds.fromArray(bounds);
}

/** 
 * APIFunction: fromArray
 * Alternative constructor that builds a new Bounds
 *     from an array
 * 
 * Parameters:
 * bbox - {Array(Float)} Array of bounds values (ex. <i>[5,42,10,45]</i>)
 *
 * Returns:
 * {<Bounds>} New <OpenLayers.Bounds> object built from the 
 *                       passed-in Array.
 */
public static function fromArray (bbox:Array):Bounds  {
    return new Bounds(parseFloat(bbox[0]),parseFloat(bbox[1]),parseFloat(bbox[2]),parseFloat(bbox[3]));
}

/** 
 * APIFunction: fromSize
 * Alternative constructor that builds a new Bounds
 *     from a size
 * 
 * Parameters:
 * size - {<Size>} 
 *
 * Returns:
 * {<Bounds>} New <Bounds> object built from the 
 *                       passed-in size.
 */
public static function fromSize (size:Size):Bounds {
    return new Bounds(0,size.h,size.w,0);
}

/**
 * Function: oppositeQuadrant
 * Get the opposite quadrant for a given quadrant string.
 *
 * Parameters:
 * quadrant - {String} two character quadrant shortstring
 *
 * Returns:
 * {String} The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
 *          you pass in "bl" it returns "tr", if you pass in "br" it 
 *          returns "tl", etc.
 */
public static function oppositeQuadrant (quadrant:String):String {
    var opp :String= "";
    
    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
    
    return opp;
}
    /**
     * Property: left
     * {Number}
     */
    public var left:Number;

    /**
     * Property: bottom
     * {Number}
     */
    public var bottom: Number;

    /**
     * Property: right
     * {Number}
     */
    public var right: Number;

    /**
     * Property: top
     * {Number}
     */
    public var top: Number; 

    /**
     * Constructor: Bounds
     * Construct a new bounds object.
     *
     * Parameters:
     * left - {Number} The left bounds of the box.  Note that for width
     *        calculations, this is assumed to be less than the right value.
     * bottom - {Number} The bottom bounds of the box.  Note that for height
     *          calculations, this is assumed to be more than the top value.
     * right - {Number} The right bounds.
     * top - {Number} The top bounds.
     */
     public function Bounds(left:Number, bottom:Number, right:Number, top:Number) {

            this.left = left;


            this.bottom = bottom;


            this.right = right;


            this.top = top;

    }

    /**
     * Method: clone
     * Create a cloned instance of this bounds.
     *
     * Returns:
     * {<Bounds>} A fresh copy of the bounds
     */
    public function clone():Bounds {
        return new Bounds(this.left, this.bottom, 
                                     this.right, this.top);
    }

    /**
     * Method: equals
     * Test a two bounds for equivalence
     *
     * Parameters:
     * bounds - {<Bounds>}
     *
     * Returns:
     * {Boolean} The passed-in Bounds object has the same left,
     *           right, top, bottom components as this.  Note that if bounds 
     *           passed in is null, returns false.
     */
    public function equals(bounds:Bounds) :Boolean{
        var equals:Boolean = false;
        if (bounds != null) {
            equals = ((this.left == bounds.left) && 
                      (this.right == bounds.right) &&
                      (this.top == bounds.top) && 
                      (this.bottom == bounds.bottom));
        }
        return equals;
    }

    /** 
     * APIMethod: toString
     * 
     * Returns:
     * {String} String representation of OpenLayers.Bounds object. 
     *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
     */
    public function toString():String {
        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
                 + " right-top=(" + this.right + "," + this.top + ")" );
    }

    /**
     * APIMethod: toArray
     *
     * Returns:
     * {Array} array of left, bottom, right, top
     */
    public function toArray():Array {
        return [this.left, this.bottom, this.right, this.top];
    }    

    /** 
     * APIMethod: toBBOX
     * 
     * Parameters:
     * decimal - {Integer} How many significant digits in the bbox coords?
     *                     Default is 6
     * 
     * Returns:
     * {String} Simple String representation of OpenLayers.Bounds object.
     *          (ex. <i>"5,42,10,45"</i>)
     */
    public function toBBOX(decimal:int=6):String {
        var mult:Number = Math.pow(10, decimal);
        var bbox:String = Math.round(this.left * mult) / mult + "," + 
                   Math.round(this.bottom * mult) / mult + "," + 
                   Math.round(this.right * mult) / mult + "," + 
                   Math.round(this.top * mult) / mult;

        return bbox;
    }
    
    /**
     * APIMethod: getWidth
     * 
     * Returns:
     * {Float} The width of the bounds
     */
    public function getWidth():Number {
        return (this.right - this.left);
    }

    /**
     * APIMethod: getHeight
     * 
     * Returns:
     * {Float} The height of the bounds
     */
    public function getHeight():Number {
        return (this.top - this.bottom);
    }

    /**
     * APIMethod: getSize
     * 
     * Returns:
     * {<Size>} An <Size> which represents the size of the box
     */
    public function getSize():Size {
        return new Size(this.getWidth(), this.getHeight());
    }

    /**
     * APIMethod: getCenterPixel
     * 
     * Returns:
     * {<Pixel>} An <Pixel> which represents the center 
     *                      of the bounds
     */
    public function getCenterPixel():Pixel {
        return new Pixel( (this.left + this.right) / 2,
                                     (this.bottom + this.top) / 2);
    }

    /**
     * APIMethod: getCenterLonLat
     * 
     * Returns:
     * {<LonLat>} An <LonLat> which represents the center 
     *                      of the bounds
     */
    public function getCenterLonLat():LonLat {
        return new LonLat( (this.left + this.right) / 2,
                                      (this.bottom + this.top) / 2);
    }

    /**
     * APIMethod: add
     * 
     * Parameters:
     * x - {Float}
     * y - {Float}
     * 
     * Returns:
     * {<Bounds>} A new <Bounds> whose coordinates are 
     *                       the same as this, but shifted by the passed-in 
     *                       x and y values
     */
    public function add(x:Number, y:Number):Bounds {

        return new Bounds(this.left + x, this.bottom + y,
                                     this.right + x, this.top + y);
    }
    
    /**
     * APIMethod: extend
     * Extend the bounds to include the point, lonlat, or bounds specified.
     * Note: This function assumes that left<right and bottom<top.
     * 
     * 
     * Parameters: 
     * object - {Object} Can be LonLat, Point, or Bounds
     */

     
    
    /* public function extend(point:Point){
     	var bounds:Bounds = new Bounds(lonlat.lon, lonlat.lat,
                                                    lonlat.lon, lonlat.lat);
        this.extend(bounds)
     }*/
     
     public function extend(object:Object):void{
     	var bounds:Bounds;
     	if(object is LonLat){
     		bounds = new Bounds(object.lon, object.lat,
                                                    object.lon, object.lat);
     	}else{
     		bounds=object as Bounds;
     	}

     	if (bounds) {
                if (bounds.left < this.left) {
                    this.left = bounds.left;
                }
                if ( bounds.bottom < this.bottom ) {
                    this.bottom = bounds.bottom;
                } 
                if ( bounds.right > this.right ) {
                    this.right = bounds.right;
                }
                if ( bounds.top > this.top ) { 
                    this.top = bounds.top;
                }
            }
     }


    /**
     * APIMethod: containsLonLat
     * 
     * Parameters:
     * ll - {<LonLat>}
     * inclusive - {Boolean} Whether or not to include the border. 
     *                       Default is true.
     *
     * Returns:
     * {Boolean} Whether or not the passed-in lonlat is within this bounds.
     */
    public function containsLonLat(ll:LonLat, inclusive:Boolean):Boolean {
        return this.contains(ll.lon, ll.lat, inclusive);
    }

    /**
     * APIMethod: containsPixel
     * 
     * Parameters:
     * px - {<Pixel>}
     * inclusive - {Boolean} Whether or not to include the border. 
     *                       Default is true.
     *
     * Returns:
     * {Boolean} Whether or not the passed-in pixel is within this bounds.
     */
    public function containsPixel(px:Pixel, inclusive:Boolean):Boolean {
        return this.contains(px.x, px.y, inclusive);
    }
    
    /**
     * APIMethod: contains
     * 
     * Parameters:
     * x - {Float}
     * y - {Float}
     * inclusive - {Boolean} Whether or not to include the border. 
     *                       Default is true.
     *
     * Returns:
     * {Boolean} Whether or not the passed-in coordinates are within this
     *           bounds.
     */
    public function contains(x:Number, y:Number, inclusive:Boolean):Boolean {
    

        
        var contains:Boolean = false;
        if (inclusive) {
            contains = ((x >= this.left) && (x <= this.right) && 
                        (y >= this.bottom) && (y <= this.top));
        } else {
            contains = ((x > this.left) && (x < this.right) && 
                        (y > this.bottom) && (y < this.top));
        }              
        return contains;
    }

    /**
     * APIMethod: intersectsBounds
     * 
     * Parameters:
     * bounds - {<Bounds>}
     * inclusive - {<Boolean>} Whether or not to include the border. 
     *                         Default is true.
     *
     * Returns:
     * {Boolean} Whether or not the passed-in OpenLayers.Bounds object 
     *           intersects this bounds. Simple math just check if either 
     *           contains the other, allowing for partial.
     */
    public function intersectsBounds(bounds:Bounds, inclusive:Boolean):Boolean {


        var inBottom:Boolean = (bounds.bottom == this.bottom && bounds.top == this.top) ?
                    true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) || 
                           ((this.bottom > bounds.bottom) && (this.bottom < bounds.top))); 
        var inTop:Boolean = (bounds.bottom == this.bottom && bounds.top == this.top) ?
                    true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
                           ((this.top > bounds.bottom) && (this.top < bounds.top))); 
        var inRight:Boolean = (bounds.right == this.right && bounds.left == this.left) ?
                    true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
                           ((this.right > bounds.left) && (this.right < bounds.right))); 
        var inLeft:Boolean = (bounds.right == this.right && bounds.left == this.left) ?
                    true : (((bounds.left > this.left) && (bounds.left < this.right)) || 
                           ((this.left > bounds.left) && (this.left < bounds.right))); 

        return (this.containsBounds(bounds, true, inclusive) ||
                bounds.containsBounds(this, true, inclusive) ||
                ((inTop || inBottom ) && (inLeft || inRight )));
    }
    
    /**
     * APIMethod: containsBounds
     * 
     * bounds - {<Bounds>}
     * partial - {<Boolean>} If true, only part of passed-in 
     *                       <Bounds> needs be within this bounds. 
     *                       If false, the entire passed-in bounds must be
     *                       within. Default is false
     * inclusive - {<Boolean>} Whether or not to include the border. 
     *                         Default is true.
     *
     * Returns:
     * {Boolean} Whether or not the passed-in OpenLayers.Bounds object is 
     *           contained within this bounds. 
     */
    public function containsBounds(bounds:Bounds, partial:Boolean, inclusive:Boolean=true):Boolean {



        var inLeft:Boolean;
        var inTop:Boolean;
        var inRight:Boolean;
        var inBottom:Boolean;
        
        if (inclusive) {
            inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
            inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
            inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
            inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
        } else {
            inLeft = (bounds.left > this.left) && (bounds.left < this.right);
            inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
            inRight= (bounds.right > this.left) && (bounds.right < this.right);
            inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
        }
        
        return (partial) ? (inTop || inBottom ) && (inLeft || inRight ) 
                         : (inTop && inLeft && inBottom && inRight);
    }

    /** 
     * APIMethod: determineQuadrant
     * 
     * Parameters:
     * lonlat - {<LonLat>}
     * 
     * Returns:
     * {String} The quadrant ("br" "tr" "tl" "bl") of the bounds in which 
     *          the coordinate lies.
     */
    public function determineQuadrant(lonlat:LonLat):String {
    
        var quadrant:String = "";
        var center:LonLat = this.getCenterLonLat();
        
        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
    
        return quadrant; 
    }

    /**
     * APIMethod: wrapDateLine
     *  
     * Parameters:
     * maxExtent - {<OpenLayers.Bounds>}
     * options - {Object} Some possible options are:
     *                    leftTolerance - {float} Allow for a margin of error 
     *                                            with the 'left' value of this 
     *                                            bound.
     *                                            Default is 0.
     *                    rightTolerance - {float} Allow for a margin of error 
     *                                             with the 'right' value of 
     *                                             this bound.
     *                                             Default is 0.
     * 
     * Returns:
     * {<Bounds>} A copy of this bounds, but wrapped around the 
     *                       "dateline" (as specified by the borders of 
     *                       maxExtent). Note that this function only returns 
     *                       a different bounds value if this bounds is 
     *                       *entirely* outside of the maxExtent. If this 
     *                       bounds straddles the dateline (is part in/part 
     *                       out of maxExtent), the returned bounds will be 
     *                       merely a copy of this one.
     */
   public  function wrapDateLine(maxExtent:Bounds, options:Object):Bounds {    
       // options = options || {};
        
        var leftTolerance:Number = options.leftTolerance || 0;
        var rightTolerance:Number = options.rightTolerance || 0;

        var newBounds:Bounds = this.clone();
    
        if (maxExtent) {

           //shift right?
           while ( newBounds.left < maxExtent.left && 
                   (newBounds.right - rightTolerance) <= maxExtent.left ) { 
                newBounds = newBounds.add(maxExtent.getWidth(), 0);
           }

           //shift left?
           while ( (newBounds.left + leftTolerance) >= maxExtent.right && 
                   newBounds.right > maxExtent.right ) { 
                newBounds = newBounds.add(-maxExtent.getWidth(), 0);
           }
        }
                
        return newBounds;
    }
	}
}